package synchronicity.host;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DataElement;
import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice;
import javax.bluetooth.ServiceRecord;
import javax.bluetooth.UUID;
import javax.microedition.io.Connector;
import javax.microedition.io.StreamConnection;
import javax.microedition.io.StreamConnectionNotifier;

import synchronicity.agent.Agent;
import synchronicity.agent.AgentFactory;

import com.moxaic.util.Empowered;

/**
 * Enables to a SchedulerHostStub to communicate with a SchedulerHost via
 * BlueTooth.
 */
public class SchedulerHostSkeleton implements Runnable {

	public static final UUID SERVER_UUID = new UUID(
			"F0E0D0C0B0A000908070605040302010", false);

	public static final int ATTRIBUTE_ID = 0x4321;

	private SchedulerHost host;

	private Thread thread;

	private boolean running;

	// private ServerSocketConnection server;

	volatile LocalDevice localDevice;

	StreamConnectionNotifier notifier;

	ServiceRecord record;

	ClientProcessor processor;

	public SchedulerHostSkeleton(SchedulerHost host) throws IOException {
		System.out.println("Creating skeleton");
		this.host = host;
		processor = new ClientProcessor();
		thread = new Thread(this, "TCPAgentTransporter");
		thread.start();
	}

	public void run() {
		running = true;
		// See BTImageServer.java in BlueToothDemo project
		try {
			synchronized (this) {
				localDevice = LocalDevice.getLocalDevice();
				notifyAll();
			}
			if (!localDevice.setDiscoverable(DiscoveryAgent.GIAC)) {
			}
		} catch (BluetoothStateException btse) {
			btse.printStackTrace();
			throw new IllegalStateException("Bluetooth unavailable!");
		}
		StringBuffer url = new StringBuffer("btspp://");
		url.append("localHost").append(':');
		url.append(SERVER_UUID.toString());
		url.append(";name=SchedulerHost");
		url.append(";authorize=false");
		try {
			notifier = (StreamConnectionNotifier) Connector
					.open(url.toString());
		} catch (IOException ioe) {
			ioe.printStackTrace();
			throw new RuntimeException();
		}
		record = localDevice.getRecord(notifier);

		DataElement base = new DataElement(DataElement.DATSEQ);
		record.setAttributeValue(ATTRIBUTE_ID, base);
		StreamConnection conn = null;
		while (running) {
			try {
				conn = notifier.acceptAndOpen();
				processor.handleConnection(conn);
			} catch (IOException ioe) {
				ioe.printStackTrace();
			}
		}
	}

	public void shutDown() {
		running = false;
	}

	private class ClientProcessor extends Empowered {
		protected void cleanup() {
		}

		protected void init() {
		}

		protected void process(Object data) {
			try {
				StreamConnection conn = (StreamConnection) data;
				Agent agent = null;
				DataInputStream in = conn.openDataInputStream();
				DataOutputStream out = conn.openDataOutputStream();
				String type = in.readUTF();
				agent = AgentFactory.createNewAgent(type);
				System.out.println("Reading agent "
						+ System.currentTimeMillis());
				agent.read(in);
				System.out.println("Visiting " + agent + " "
						+ System.currentTimeMillis());
				agent = host.traverse(agent);
				System.out.println("Writing agent " + agent + " "
						+ System.currentTimeMillis());
				agent.write(out);
				out.flush();
				in.close();
				out.close();
				conn.close();
			} catch (IOException ioe) {
				ioe.printStackTrace();
			} catch (NullPointerException npe) {
				npe.printStackTrace();
			}
		}

		public void handleConnection(StreamConnection conn) {
			addData(conn);
			performOp();
		}
	}

	public synchronized String getHostIdentifier() {
		if (localDevice == null) {
			try {
				wait();
			} catch (InterruptedException ie) {
			}
		}
		return localDevice.getBluetoothAddress();
	}
}
